/**
 * 
 */
package taogenie.util;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.MessageDigest;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Random;
import java.util.Map.Entry;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

/**
 * @author taogenie
 * 
 */
public class FileUtils {
	public static final int BUFFERSIZE = 1048576;

	public static char[] hexChar = { '0', '1', '2', '3', '4', '5', '6', '7',
			'8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
	public static Map<String, String> hashMap = new HashMap<String, String>();

	public static String toHexString(byte[] b) {
		StringBuilder sb = new StringBuilder(b.length * 2);
		for (int i = 0; i < b.length; i++) {
			sb.append(hexChar[(b[i] & 0xf0) >>> 4]);
			sb.append(hexChar[b[i] & 0x0f]);
		}
		return sb.toString();
	}

	public static String getHash(String fileName) throws Exception {
		InputStream fis;
		fis = new FileInputStream(fileName);
		byte[] buffer = new byte[1024];
		MessageDigest md5 = MessageDigest.getInstance("MD5");
		int numRead = 0;
		while ((numRead = fis.read(buffer)) > 0) {
			md5.update(buffer, 0, numRead);
		}
		fis.close();
		return toHexString(md5.digest());
	}

	public static void removeDuplicate(File dir) throws Exception {
		new File("result").mkdir();
		File files[] = dir.listFiles();
		int listlen = files.length;
		for (int i = 0; i < listlen; i++) {
			if (files[i].isDirectory()) {
				removeDuplicate(files[i]);
			} else {
				String hash = getHash(files[i].getAbsolutePath());
				File dest = new File("./result/" + hash);
				if (dest.exists()) {
					int j = 1;
					do {
						dest = new File("./result/" + hash + "-" + j);
						j++;
					} while (dest.exists());
					files[i].renameTo(dest);
				} else {
					files[i].renameTo(dest);
				}
			}
		}
		dir.delete();
	}

	public static void reportDuplicateDir(File dir) throws Exception {
		File files[] = dir.listFiles();
		int listlen = files.length;
		for (int i = 0; i < listlen; i++) {
			if (files[i].isDirectory()) {
				reportDuplicateDir(files[i]);
			} else {
				String hash = getHash(files[i].getAbsolutePath());
				String value = hashMap.get(hash);
				if (value == null) {
					value = files[i].getAbsolutePath();
				} else {
					value += ";" + files[i].getAbsolutePath();
				}
				hashMap.put(hash, value);
			}
		}
		dir.delete();
	}

	public static void reportDuplicate(File dir) throws Exception {
		reportDuplicateDir(dir);
		Iterator<Entry<String, String>> iter = hashMap.entrySet().iterator();
		while (iter.hasNext()) {
			Entry<String, String> entry = iter.next();
			System.out.println(entry.getKey() + ":   " + entry.getValue());
		}
	}

	public static boolean isZipFile(File file) throws IOException {
		ZipInputStream zis = new ZipInputStream(new FileInputStream(file));
		ZipEntry entry = zis.getNextEntry();
		System.out.println(entry);
		zis.close();
		return !(entry == null);
	}

	public static void delNonZipFiles(String folder) throws IOException {
		File files[] = new File(folder).listFiles();
		for (File file : files) {
			System.out.println(file.getAbsolutePath());
			if (file.isDirectory()) {
				delNonZipFiles(file.getAbsolutePath());
			} else {
				if (!isZipFile(file)) {
					while (file.delete() == false) {
						System.err.println(file.getAbsolutePath());
					}
				}
			}
		}
	}

	public static void moveToSubDirs(String folder, int fileNumPerDir) {
		File[] files = new File(folder).listFiles();
		int fileNum = 0;
		String folderName = null;
		int randomNo;
		for (File file : files) {
			System.out.println(file.getAbsolutePath());
			if (fileNum % fileNumPerDir == 0) {
				randomNo = new Random().nextInt() % 1000;
				if (randomNo < 0) {
					randomNo = -randomNo;
				}
				randomNo = (int) (randomNo * 100000 + (new Date().getTime() % 100000));
				folderName = folder + "\\" + randomNo + "\\";
				new File(folderName).mkdir();
			}
			fileNum++;
			file.renameTo(new File(folderName + file.getName()));
		}
	}

	public static void mergeSubDirs(String checkFolder, String targetFolder) {
		File[] files = new File(checkFolder).listFiles();
		for (File file : files) {
			System.out.println(file.getAbsolutePath());
			if (file.isFile()) {
				file.renameTo(new File(targetFolder + "\\"
						+ (new Date().getTime())));
			} else {
				mergeSubDirs(file.getAbsolutePath(), targetFolder);
			}
		}
		if (checkFolder.compareTo(targetFolder) != 0) {
			new File(checkFolder).delete();
		}

	}

	public static void mergeSubDirsWithRename(String checkFolder,
			String targetFolder) {
		File[] files = new File(checkFolder).listFiles();
		if (files != null) {
			for (File file : files) {
				System.out.println(file.getAbsolutePath());
				if (file.isFile()) {
					file.renameTo(new File(targetFolder + "\\"
							+ (new Date().getTime())));
				} else {
					mergeSubDirsWithRename(file.getAbsolutePath(), targetFolder);
				}
			}
		}
		if (checkFolder.compareTo(targetFolder) != 0) {
			new File(checkFolder).delete();
		}
	}

	public static void extractDexFiles(String srcFolder, String dstFolder)
			throws IOException {
		File[] files = new File(srcFolder).listFiles();
		ZipInputStream zis = null;
		for (File file : files) {
			// System.out.println(file.getAbsolutePath());
			if (file.isFile()) {
				try {
					zis = new ZipInputStream(new BufferedInputStream(
							new FileInputStream(file)));
					ZipEntry entry;
					int count;
					while ((entry = zis.getNextEntry()) != null) {
						if (entry.getName().compareTo("classes.dex") == 0) {
							byte data[] = new byte[BUFFERSIZE];
							FileOutputStream fos = new FileOutputStream(
									dstFolder + "\\" + file.getName() + ".dex");
							BufferedOutputStream dest = new BufferedOutputStream(
									fos, BUFFERSIZE);
							while ((count = zis.read(data, 0, BUFFERSIZE)) != -1) {
								dest.write(data, 0, count);
							}
							dest.flush();
							dest.close();
							break;
						}
					}
					zis.close();
				} catch (Exception e) {
					if (zis != null) {
						zis.close();
					}
					System.err.println(file.getAbsolutePath());
					e.printStackTrace();
					file.renameTo(new File("C:\\APKS\\delete\\"
							+ file.getName()));
				}
			}
		}
	}

	public static void extractDexFolder(String srcFolder, String dstFolder)
			throws IOException {
		File[] folders = new File(srcFolder).listFiles();
		for (File folder : folders) {
			if (folder.isDirectory()) {
				System.out.println(folder.getAbsolutePath());
				new File(dstFolder + "\\" + folder.getName()).mkdir();
				extractDexFiles(folder.getAbsolutePath(), dstFolder + "\\"
						+ folder.getName());
			}
		}
	}

	public static void copyFolderStructure(String srcFolder, String dstFolder) {
		File[] files = new File(srcFolder).listFiles();
		for (File file : files) {
			if (file.isDirectory()) {
				new File(dstFolder + "\\" + file.getName()).mkdir();
			}
		}
	}

	public static void moveFileAccordingToStructure(String srcFolder,
			String fromFolder, String toFolder) {
		// read src structure
		Map<String, String> structure = new HashMap<String, String>();
		File[] folders = new File(srcFolder).listFiles();
		File[] files;
		String folderName;
		for (File folder : folders) {
			if (folder.isDirectory()) {
				System.out.println(folder.getAbsolutePath());
				folderName = folder.getName();
				files = folder.listFiles();
				for (File file : files) {
					structure.put(file.getName(), folderName);
				}
			}
		}

		// move
		folders = new File(fromFolder).listFiles();
		for (File folder : folders) {
			if (folder.isDirectory()) {
				files = folder.listFiles();
				for (File file : files) {
					if (file.getName().endsWith("Google")) {
						System.out.println(file.getAbsolutePath());
						folderName = structure.get(file.getName());
						if (folderName == null) {
							file.renameTo(new File(toFolder + "\\"
									+ file.getName()));
						} else {
							file.renameTo(new File(toFolder + "\\" + folderName
									+ "\\" + file.getName() + ".dex"));
						}
					}
				}
			}
		}
	}

	public static void extractCert(String srcFolder, String dstFolder) {
		File[] files = new File(srcFolder).listFiles();
		boolean isFound;
		for (File file : files) {
			try {
				// System.out.println(file.getName());
				ZipInputStream zis = new ZipInputStream(new FileInputStream(
						file));
				ZipEntry entry;
				isFound = false;
				while ((entry = zis.getNextEntry()) != null) {
					if (entry.getName().toLowerCase().endsWith(".rsa")) {
						isFound = true;
						byte data[] = new byte[BUFFERSIZE];
						if (new File(dstFolder
								+ "\\"
								+ file.getName().substring(0,
										file.getName().length() - 4) + ".rsa")
								.exists()) {
							System.err.println(file.getAbsolutePath());
						}
						FileOutputStream fos = new FileOutputStream(dstFolder
								+ "\\"
								+ file.getName().substring(0,
										file.getName().length() - 4) + ".rsa");
						BufferedOutputStream dest = new BufferedOutputStream(
								fos, BUFFERSIZE);
						int count;
						while ((count = zis.read(data, 0, BUFFERSIZE)) != -1) {
							dest.write(data, 0, count);
						}
						dest.flush();
						dest.close();
					}

				}
				if (!isFound) {
					System.err.println(file.getAbsolutePath());
				}
				zis.close();
			} catch (Exception e) {
				System.out.println(file.getAbsolutePath());
				e.printStackTrace();
			}
		}
	}

	public static void main(String[] args) throws Exception {
		// delNonZipFiles("C:\\APKS\\hiapk");
		// moveToSubDirs("C:\\APKS\\GOOGLE",300);
		extractDexFiles("C:\\APKS\\AndroidSamples", "C:\\APKS\\virus");
		// mergeSubDirs("C:\\APKS\\GOOGLE","C:\\APKS\\GOOGLE");
		// copyFolderStructure("E:\\APKS\\GOOGLE","E:\\DEXS\\GOOGLE");
		// moveFileAccordingToStructure("E:\\APKS\\GOOGLE","E:\\DEXS_MESS","E:\\DEXS\\GOOGLE");
		// extractDexFolder("C:\\APKS\\hiapk","C:\\APKS\\dex");
		// mergeSubDirsWithRename("C:\\APKS\\google","C:\\APKS\\google");
	}

}
